#include <string.h> 
#include <avr/io.h>

#include "../avrx/avrx.h"

#include "rtc.h"
#include "disp.h"
#include "maintask.h"

/*
     a
   f   b
	 g
   e   c
     d   h
*/

static const unsigned char digi_map_t[] = 
{
   /* hgfedcba */
    0b00111111,	/*0*/
	0b00000110,	/*1*/
    0b01011011,	/*2*/
    0b01001111, /*3*/
    0b01100110, /*4*/
    0b01101101, /*5*/
    0b01111101, /*6*/
    0b00000111, /*7*/
    0b01111111, /*8*/
    0b01101111, /*9*/
	0b10000000, /*.,10*/
	0b00000001, /*_,11*/
	0b00000010, /*-,12*/
	0b00000011, /*:,13*/
	0b00000100, /*',14*/
	0b01000000, /*-,15*/
	0b01010100, /*n,16*/
	0b01110001, /*f,17*/
	0b01011110, /*d,18*/
    0b00000000,  /*all on,19*/
	0b11111111  /*all off,20*/
};

#define LED_SEG_PORT PORTA
#define LED_SEG_DIR  DDRA

#define LED_POS_PORT PORTB
#define LED_POS_DIR  DDRB

#define BLACK_OUT_TIME 18000 //10x18000 = 180000 milisec = 3 minue

sSDisplayMessage 	sDispCharMessage;           	// The message control
MessageQueue 		sDisplayQueue;           		// The message queue

static TimerControlBlock sDisplayTimer;		// The Timer queue    

unsigned char SegmenPos = 0;	
char DispSegmenBuffer[SEGMENT_DIGIT_MAX];
char DispSegmenData[SEGMENT_DIGIT_MAX];
char DispSegmenUpdate = FALSE;


unsigned char Dec2Seg(unsigned char);
void doDispCharMessage(psSDisplayChar);
void runDisplay( void );

unsigned char cSegmenOpt = 0;
unsigned char cSegmentToggle = 0;
unsigned char cSegmentOptChar = SEGMENT_BLINK_ON;
unsigned int  uiDisplayBlackOutTime = BLACK_OUT_TIME;
unsigned char cDisplayBlackOutFlag = FALSE;
unsigned char cDisplayBlackOutOn = TRUE;

/*
	,time.year
	,time.month
	,time.date
	,time.day
	,time.hour
	,time.minutes
	,time.seconds
*/
/*
void SetTimeDisp(RTC_t time)
{

    DispSegmenData[0] = (time.hour    / 10);
    DispSegmenData[1] = (time.hour    % 10);
    DispSegmenData[2] = (time.minutes / 10);
    DispSegmenData[3] = (time.minutes % 10);
    DispSegmenData[4] = 10;

    DispSegmenUpdate = TRUE;

}
*/

unsigned char Dec2Seg(unsigned char value)
{

    if( value >= 0 && value <=sizeof(digi_map_t) )
    {
        return ~digi_map_t[value];
    }
    return 0;

}

#define DIDP_MOTION_TIME_INTERVAL 25
unsigned char ucDispMotionTime = DIDP_MOTION_TIME_INTERVAL;
char cDispMotionData[SEGMENT_DIGIT_MAX];
unsigned char ucDispMotionFlag = FALSE;
unsigned char ucDispMotionPosition = 0;

#define DISP_MOTION_BUFF_SIZE 10
struct SDispMotionBuffer
{
	char cData[SEGMENT_DIGIT_MAX];	
	char cOpt;	
	
} 
sDispMotionBuffer[DISP_MOTION_BUFF_SIZE];
unsigned char sDisplayMotionBuffIn;
unsigned char sDisplayMotionBuffOut;


void InitMotionDispBuffer( void )
{
	unsigned char ucCount = 0;
	
	for( ucCount = 0;ucCount < DISP_MOTION_BUFF_SIZE; ucCount++)
	{
		memset((void *)&sDispMotionBuffer[ucCount].cData,0xff,DISP_MOTION_BUFF_SIZE);
		sDispMotionBuffer[ucCount].cOpt = 0x00;
	}
	
	sDisplayMotionBuffIn  = 0;
	sDisplayMotionBuffOut = 0;
	
}

int DispMotionBuffEmpty( void )
{
	if (sDisplayMotionBuffIn == sDisplayMotionBuffOut)	// isEmpty()
		return TRUE;
	else
		return FALSE;
}

int PutDispMotionBuff(char * ptr, char cOpt)
{
	unsigned char in;
	
	in = sDisplayMotionBuffIn+1;
	
	if (in >= DISP_MOTION_BUFF_SIZE)
		in = 0;
	if (in == sDisplayMotionBuffOut)	// isFull()
		return FALSE;

	memcpy((void *)&sDispMotionBuffer[sDisplayMotionBuffIn].cData, (const void *)ptr, SEGMENT_DIGIT_MAX);
	sDispMotionBuffer[sDisplayMotionBuffIn].cOpt = cOpt;
	
	sDisplayMotionBuffIn = in;
	
	return TRUE;
	
}

void PopDispMotionBuff(char * ptr,char * pOpt)
{
	unsigned char out;
		
	memcpy((void *)ptr, (void *)&sDispMotionBuffer[sDisplayMotionBuffOut].cData, SEGMENT_DIGIT_MAX);
	*pOpt = sDispMotionBuffer[sDisplayMotionBuffOut].cOpt;
	
	out = sDisplayMotionBuffOut+1;
	if (out >= DISP_MOTION_BUFF_SIZE)
		out = 0;
	sDisplayMotionBuffOut = out;		
}


void processDisplayMotion( void )
{
	signed char ucCount;
	
	if( ucDispMotionTime > DIDP_MOTION_TIME_INTERVAL )
	{		
		ucDispMotionTime = 0;
		if( ucDispMotionPosition == 0)
		{			
			memset((void *)&DispSegmenData,SEGMENT_ALL_OFF,SEGMENT_DIGIT_MAX);
			ucDispMotionPosition++;
			DispSegmenUpdate = TRUE;				
		}
		else if( ucDispMotionPosition < SEGMENT_DIGIT_MAX )
		{			
			for( ucCount=SEGMENT_DIGIT_MAX; ucCount > 0; ucCount-- )
			{
				if( ucCount > ucDispMotionPosition )
				{
					DispSegmenData[ucCount-1] = SEGMENT_ALL_OFF;
				}
				else
				{
					DispSegmenData[ucCount-1] = cDispMotionData[3 - (ucDispMotionPosition - ucCount)];
				}					
			}				
			ucDispMotionPosition++;
			DispSegmenUpdate = TRUE;				
		}
		else
		{
			ucDispMotionPosition = 0;
			DispSegmenData[SEGMENT_DIGIT_MAX-1] = cDispMotionData[SEGMENT_DIGIT_MAX-1];
			DispSegmenUpdate = TRUE;				
			ucDispMotionFlag = FALSE;
			ucDispMotionTime = DIDP_MOTION_TIME_INTERVAL;
		}
	}
	else
		ucDispMotionTime++; 

}

void processBlackout(void)
{
	if( uiDisplayBlackOutTime > 0)
	{
		uiDisplayBlackOutTime--;
		cDisplayBlackOutFlag = FALSE;
	}
	else
	{
		if(cDisplayBlackOutOn == TRUE )
			cDisplayBlackOutFlag = TRUE;
		else
			cDisplayBlackOutFlag = FALSE;
	}

}
/*
#define DISP_CMM_BUFF_SIZE 10
sSDisplayChar sDisplayCmdBuff[DISP_CMM_BUFF_SIZE];
unsigned char sDisplayCmdBuffIn;
unsigned char sDisplayCmdBuffOut;


void InitDispActCmd( void )
{
	unsigned char ucCount = 0;
	
	for( ucCount = 0;ucCount < DISP_CMM_BUFF_SIZE; ucCount++)
	{
		sDisplayCmdBuff[ucCount].act = 0xff;
		memset((void *)&sDisplayCmdBuff[sDisplayCmdBuffIn].m_segment,0xff,SEGMENT_DIGIT_MAX);
		sDisplayCmdBuff[ucCount].m_opt = 0xff;	
	}
	
	sDisplayCmdBuffIn  = 0;
	sDisplayCmdBuffOut = 0;
	
}

int DispActCmdEmpty( void )
{
	if (sDisplayCmdBuffIn == sDisplayCmdBuffOut)	// isEmpty()
		return TRUE;
	else
		return FALSE;
}

int PutDispActCmd(sSDisplayChar * ptr)
{
	unsigned char in;
	
	in = sDisplayCmdBuffIn+1;
	
	if (in >= DISP_CMM_BUFF_SIZE)
		in = 0;
	if (in == sDisplayCmdBuffOut)	// isFull()
		return FALSE;

	sDisplayCmdBuff[sDisplayCmdBuffIn].act = ptr->act;
	memcpy((void *)&sDisplayCmdBuff[sDisplayCmdBuffIn].m_segment, (const void *)ptr->m_segment, SEGMENT_DIGIT_MAX);
	sDisplayCmdBuff[sDisplayCmdBuffIn].m_opt = ptr->m_opt;
		
	sDisplayCmdBuffIn = in;
	
	return TRUE;
	
}

sSDisplayChar PopDispActCmd( void )
{
	sSDisplayChar m_sdata;
	unsigned char out;
		
	m_sdata = sDisplayCmdBuff[sDisplayCmdBuffOut];
	
	out = sDisplayCmdBuffOut+1;
	if (out >= DISP_CMM_BUFF_SIZE)
		out = 0;
	sDisplayCmdBuffOut = out;
	
	
	return m_sdata;
}
*/
//#define DISP_PROCESS_TIME_INTERVAL 1000
//unsigned int ucDispProcessTimeInterval = DISP_PROCESS_TIME_INTERVAL;
void processDisplay( void )
{	
	if(ucDispMotionFlag == TRUE)
	{
		processDisplayMotion();
	}
	else 
	{
		// if( ucDispProcessTimeInterval > DISP_PROCESS_TIME_INTERVAL )
		// {
			if(DispMotionBuffEmpty() == FALSE)
			{
				PopDispMotionBuff((char *)&cDispMotionData,(char *)&cSegmenOpt);
				ucDispMotionFlag = TRUE;		
				//ucDispProcessTimeInterval = 0; //start process time interval
			}			
		// }
		// else
			// ucDispProcessTimeInterval++;
	}	
    
	processBlackout();
		
	
}

void runDisplay( void )
{
	char led_pos_pre = LED_POS_PORT;
	char led_segment = 0xff;
	
	cSegmentToggle++;
	if( cSegmentToggle >= (200-50))
	{
		if( cSegmentToggle == 200 )
		{	
			cSegmentToggle = 0;
			cSegmentOptChar = SEGMENT_BLINK_ON;
		}
		else
		{
			cSegmentOptChar = SEGMENT_BLINK_OFF;
		}
	}
	
	if( (cSegmenOpt & (0x01 << SegmenPos))  != 0 )
	{
		if( cSegmentOptChar == SEGMENT_BLINK_ON)
		{
			led_segment = Dec2Seg( DispSegmenBuffer[SegmenPos] );			
		}
		else
		{
			//cCharDisplay = SEGMENT_ALL_OFF;		
			led_segment = Dec2Seg( SEGMENT_ALL_OFF );	
		}		
	}
	else
	{
		//cCharDisplay = DispSegmenBuffer[SegmenPos];		
		led_segment = Dec2Seg( DispSegmenBuffer[SegmenPos] );	
	}

	if(cDisplayBlackOutFlag == TRUE)
	{	
		led_segment |= 0xff;
	}
	
	if(SegmenPos ==  0)
	{
		led_segment &= 0x7f;
	}
	
	LED_SEG_PORT = led_segment;
	LED_POS_PORT = ((led_pos_pre & 0xe0) | (0x01 << SegmenPos));

	
	if( ++SegmenPos >= SEGMENT_DIGIT_MAX )
	{
		SegmenPos = 0;
	   
		if( DispSegmenUpdate == TRUE )
		{
			memcpy((void *)&DispSegmenBuffer,(const void *)&DispSegmenData,SEGMENT_DIGIT_MAX);
			DispSegmenUpdate = FALSE;
		}

	}
}

void doDispCharMessage(psSDisplayChar p)
{

	if( p->act == 2 )
	{
		PutDispMotionBuff((char *)&p->m_segment, p->m_opt);
		// memcpy(&cDispMotionData, (const void *)p->m_segment, sizeof(DispSegmenData));
		// cSegmenOpt = p->m_opt;
		// ucDispMotionFlag = TRUE;	
	}
	else
	{	 
		memcpy(&DispSegmenData, (const void *)p->m_segment, sizeof(DispSegmenData));
		cSegmenOpt = p->m_opt;
		DispSegmenUpdate = TRUE;		
	}

	if( p->act != 0 )
	{
		uiDisplayBlackOutTime = BLACK_OUT_TIME; //turn back display
	}


}


AVRX_GCC_TASKDEF(DispTask, 35, 1)
{

	psSDisplayMessage pMsg;
	
	sDisplayTimer.semaphore = SEM_PEND;	// Need to initialize stack variables...
	
	/*Segmen Control*/
	LED_SEG_PORT =  _BV(7)|_BV(6)|_BV(5)|_BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0); //set pin to high
	LED_SEG_DIR  =  _BV(7)|_BV(6)|_BV(5)|_BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0);  //Output mode

	LED_POS_PORT &= ~(_BV(4))|~(_BV(3))|~(_BV(2))|~(_BV(1))|~(_BV(0)); //set pin to low
	LED_POS_DIR  |= _BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0);  //Output mode
	
    memset((void *)&DispSegmenBuffer,SEGMENT_ALL_ON,sizeof(DispSegmenBuffer));
    memset((void *)&DispSegmenData,SEGMENT_ALL_ON,sizeof(DispSegmenData));
	
	InitMotionDispBuffer();
	
	DispSegmenUpdate = FALSE;		
	
	AvrXStartTimerMessage((pTimerMessageBlock)&sDisplayTimer, 10, &sDisplayQueue);
	

    while(1)
    {
		pMsg = (psSDisplayMessage)AvrXWaitMessage(&sDisplayQueue);
		
		if( pMsg == &sDispCharMessage )
		{
			AvrXAckMessage( &pMsg->m_mcb );
			doDispCharMessage(pMsg->m_ptr);	
		}
		else if((pTimerControlBlock)pMsg == (pTimerControlBlock)&sDisplayTimer)
		{
			processDisplay();		
			runDisplay();
			AvrXStartTimerMessage((pTimerMessageBlock)&sDisplayTimer, 5, &sDisplayQueue);
		}
		else
		{
			AvrXHalt();
		}
				
    }
	
}
